﻿/*
 * BFind - A tool for finding football matches for trading on Betfair
 * Copyright 2009, 2010 Magnus Olsson <magols72(at)gmail.com>
 * 
 * This file is part of BFind.
 * 
 * BFind is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * BFind is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with BFind.  If not, see <http://www.gnu.org/licenses/>
*/

using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using betfairfind.Betfair.Global;
using System.Threading;

namespace betfairfind
{
    public class FindSoccerGames
    {
        public static bool DEBUG = false;
 
        Dictionary<string, string> config = new Dictionary<string, string>();

        // for testing purpose, if set to an a valid token (given from Betfair login process) this will be reused to avoid excessive logins. 
        // Otherwise (if empty string) a normal login will occur.
        private string _SESSION_TOKEN_STORED = @"";

        // these are loaded from config, replacing defaults
        public static List<string> marketnames = new List<string>();

    //    int DAYS_TO_PREFETCH = 1; // number of days ahead to check for matches    (will be phased out)
        int HOURS_TO_PREFETCH = 24; // hours ahead to check for matches
        int TIMEZONE_ADJUST = 0;

        static List<MyMarket> inplaymarkets = new List<MyMarket>();
        static Dictionary<long, MarketPricesCompressed> marketprices = new Dictionary<long, MarketPricesCompressed>();

        private const int FETCH_WAIT = 1000 * 1; // interval to wait between calling GetMarketPricesCompressed, throttled at 60/minute


        static void Main(string[] args)
        {
            FindSoccerGames mainapp = new FindSoccerGames();
        }



        public FindSoccerGames()
        {
     
            LoadConfig();

            DEBUG = bool.Parse(config["DEBUG"]);

            Logger log = new Logger(@".\", config["LOG_FILE"], config["ERRORLOG_FILE"]);
            Logger.USE_CONSOLE = true;
            Logger.Log("Starting new run at " + DateTime.Now, true, true);

          
            // print version number
            Type t = this.GetType();
            Logger.Log(t.Assembly.FullName.Split(new char[] { ',' })[1].ToString().Remove(0,1), true, true);

            LoadMarketsFromConfig();

            CheckBetfairCredentials();
            Logger.Log("Trying to log in.", true, true);
            DoLogin();

            if (DEBUG) Logger.DebugToFile("Logged in.");
            SetupCurrency();
            if (DEBUG) Logger.DebugToFile("Currency.");

            FetchInplayMarkets();
            if (DEBUG) Logger.DebugToFile("Fetched InPlay");
            FetchMarketPrices();
            if (DEBUG) Logger.DebugToFile("Fetched Prices");

            RunRules();


            Logger.Log("Finished.", true, true);
             
        }


        private void DoLogin()
        {
            if (_SESSION_TOKEN_STORED == "")
            {
              //  Logger.Log("Logging in...", true, true);
             //   Login.LOG = true;
                Login.username = config["USERNAME"];
                Login.password = config["PASSWORD"];
                Login.DoLogin();
             //   Logger.Log("Logged in. Session token: " + Login.SESSION_TOKEN, true, true);
            }
            else
            {
             //   Logger.Log("Reusing session token: " + _SESSION_TOKEN_STORED, true, true);
                Login.ReuseLogin(_SESSION_TOKEN_STORED);
            }
        }

        private void FetchInplayMarkets()
        {

      //      DAYS_TO_PREFETCH = Int32.Parse(config["DAYS_TO_CHECK"]);
            HOURS_TO_PREFETCH = Int32.Parse(config["HOURS_TO_CHECK"]);
            TIMEZONE_ADJUST = Int32.Parse(config["TIMEZONE_ADJUST"]);

            // fetch all inplay soccer markets
            Nullable<int>[] sportsid = new Nullable<int>[1] { 1 };  // soccer id
            MyMarket[] allmarkets = BF.RequestAllMarkets(sportsid, HOURS_TO_PREFETCH);


            if (allmarkets == null)
            {
                Logger.Log("No matches in the in-play coupon within " + HOURS_TO_PREFETCH + " hours.", true, true);
                System.Environment.Exit(-1);
            }

            // menupath string exclusion filter
            string[] excludeIfInPath = config["FIXTURETEXT.EXCLUDE"].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            int excludecount = 0;

            for (int i = 0; i < allmarkets.Length - 1; i++)
            {
                

                if (allmarkets[i] != null)
                {
                    // check for exclusion
                    bool exclude = false;
                    foreach (var e in excludeIfInPath)
                    {
                        if (allmarkets[i].menuPath.ToLower().Contains(e.ToLower()))
                        {
                            exclude = true;
                            excludecount++;                            
                        }
                    }


                    if (!exclude && 
                        allmarkets[i].marketGoingInPlay && 
                        marketnames.Contains(allmarkets[i].name.ToLower()))
                    {

                        // limit for testing, change "v"  to a team name
                       // if (allmarkets[i].menuPath.Contains("v"))
                        //{
                           
                        // time correction
                            allmarkets[i].marketTime = allmarkets[i].marketTime.Add(new TimeSpan(TIMEZONE_ADJUST, 0, 0));
                            inplaymarkets.Add(allmarkets[i]);
                       
                        // }


                    }

                }
            }
            Logger.Log("Fetched all markets:" + allmarkets.Length, true, true);
            if (DEBUG) Logger.DebugToFile("Fetched all markets");
            Logger.Log("Matches; " + GetMatches().Count() + " totalling " + inplaymarkets.Count + " markets", true, true);

        }


        private void FetchMarketPrices()
        {
            Logger.Log("Time for price fetch: " + inplaymarkets.Count + " seconds.", true, true);
            int counter = 1;
            foreach (var m in inplaymarkets)
            {
                marketprices.Add(m.marketId, BF.GetMarketPricesCompressed(m.marketId));
                Logger.Log("Fetched prices for market " + m.marketId + ". Time left: " + (inplaymarkets.Count - counter) + " seconds", false, true);

                counter++;
                Thread.Sleep(FETCH_WAIT);
            }
        } 

        public static string[] GetMatches()
        {
            if (DEBUG) Logger.DebugToFile("GetMatches()");

            var matches = (from m in inplaymarkets
                           orderby m.marketTime, m.marketTime
                          select m.menuPath).Distinct();

            if (DEBUG) Logger.DebugToFile("GetMatches() return " + matches.Count());
            return matches.ToArray();

        }

        public static MyMarket GetMarket(string menupath, string marketname)
        {
            var matches = (from m in inplaymarkets
                           where m.name.ToLower() == marketname.ToLower()
                           && m.menuPath == menupath
                           select m).Distinct();

            return matches.ToArray()[0];

        }


        private void LoadConfig()
        {
            foreach (var key in ConfigurationManager.AppSettings.AllKeys)
            {
                config.Add(key, ConfigurationManager.AppSettings.Get(key));
            }
        }

        private void LoadMarketsFromConfig()
        {
            string[] all = config["MarketsToFetch"].Split(new char[] {'|'});
            for (int i = 0; i < all.Length; i++)
            {
                marketnames.Add(all[i].ToLower());
            }
        }


        private void SetupCurrency()
        {
            ViewProfileResp profile = BF.GetProfile();
            string profileCurrency = profile.currency.ToUpper();
            if (profileCurrency == "USD")
            {
                config.Add("CURRENCY_SIGN", "$");
            }
            else if (profileCurrency == "GBP")
            {
                config.Add("CURRENCY_SIGN", "£");
            }
            else if (profileCurrency == "EUR")
            {
                config.Add("CURRENCY_SIGN", "€");
            }
            else
            {
                config.Add("CURRENCY_SIGN", "£");
            }
        }

        private void CheckBetfairCredentials() 
        {
            
            if (config["USERNAME"].ToString().Replace(" ", "") == "")
            {
                Console.WriteLine("");
                Console.Write("Enter Betfair username: ");
                StringBuilder sb = new StringBuilder();
                string input = "";
                while (input != "\r")
                {
                    input = Console.ReadKey(false).KeyChar.ToString();
                    if (input != "\r")
                    {
                        sb.Append(input.ToString());
                    }
                }
                config["USERNAME"] = sb.ToString();
                
            }

            if (config["PASSWORD"].ToString().Replace(" ", "") == "")
            {
                Console.WriteLine("");
                Console.Write("Betfair password: ");
                StringBuilder sb = new StringBuilder();
                string input = "";
                while (input != "\r")
                {
                    input = Console.ReadKey(true).KeyChar.ToString();
                    if (input != "\r")
                    {
                        sb.Append(input.ToString());
                        Console.Write("*");
                    }
                }
                Console.WriteLine("");
                config["PASSWORD"] = sb.ToString();
                

            }
            return;
        }

        private void RunRules()
        {
            if (config["Over15.USE"].ToLower() == "true")
            {
                Rules.Over15 rule_o15 = new Rules.Over15(config, inplaymarkets, marketprices);
            }

            if (config["Over25.USE"].ToLower() == "true")
            {
                Rules.Over25 rule_o2 = new Rules.Over25(config, inplaymarkets, marketprices);
            }

            if (config["CS001221.USE"].ToLower() == "true")
            {
                Rules.CS001221 rule_cs = new Rules.CS001221(config, inplaymarkets, marketprices);
            }

            if (config["CS0011.USE"].ToLower() == "true")
            {
                Rules.CS0011 rule_cs0011 = new Rules.CS0011(config, inplaymarkets, marketprices);
            }

            if (config["LTD.USE"].ToLower() == "true")
            {
                Rules.LTD rule_ltd = new Rules.LTD(config, inplaymarkets, marketprices);
            }
            if (config["LTD2.USE"].ToLower() == "true")
            {
                Rules.LTD2 rule_ltd2 = new Rules.LTD2(config, inplaymarkets, marketprices);
            }
            if (config["O25HT00FT00.USE"].ToLower() == "true")
            {
                Rules.O25HT00FT00 rule_O25HT00FT00 = new Rules.O25HT00FT00(config, inplaymarkets, marketprices);
            }
            if (config["U25START.USE"].ToLower() == "true")
            {
                Rules.U25START rule_U25START = new Rules.U25START(config, inplaymarkets, marketprices);
            }

            //if (config["Under25Firstgoal.USE"].ToLower() == "true")
            //{
            //    Rules.Under25Firstgoal rule_u25fg010 = new Rules.Under25Firstgoal(config, inplaymarkets, marketprices);
            //}
        }

    }
}
